package juego;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

//import javax.swing.text.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import ar.uba.fi.algo3.titiritero.Posicionable;

//esta clase se encarga de que los moviles actuen,
//y de almacenar el puntaje
public class Coordinador implements Persistible {
	
	//atributos de clase
	private static int miPuntajeABatir = 1000;
	private static String archivo = "algo42full.xml";
	
	//atributos de instancia
	private Tripulado algo42;
	private int puntaje;
	private ArrayList<Movil> listaDeMoviles = new ArrayList<Movil>();
	private ArrayList<Botin> listaDeBotines = new ArrayList<Botin>();
	private Queue<Movil> colaMovilesParaAgregar;
	private int cantidadDeVidas;
	

	//metodos
	
	//constructor.
	//recibe una referencia al algo42.
	//inicializa puntaje en 0.
	public Coordinador(Tripulado tripulado){
		this.puntaje = 0;
		this.algo42 = tripulado;
		this.listaDeMoviles.add(tripulado);
		this.colaMovilesParaAgregar = new LinkedList<Movil>();
		this.cantidadDeVidas = 3;
	}
	
	public Coordinador(ArrayList<Movil> moviles,ArrayList<Botin> botines,int punt){
		this.puntaje = punt;
		//this.algo42 = tripulado;
		this.listaDeBotines = botines;
		this.listaDeMoviles = moviles;
		this.algo42 = (Tripulado) listaDeMoviles.get(0);
		//this.listaDeMoviles.add(tripulado);
		this.colaMovilesParaAgregar = new LinkedList<Movil>();
		this.cantidadDeVidas = 3;
	}


	
	//devuelve true si el alo42 esta muerto
	public boolean murio(){
		return !this.algo42.estaVivo();
	}
	
	public int verPuntaje(){
		return this.puntaje;
	}
	
	//devuelve true si se alcanzo o supero el puntaje a batir
	public boolean puntajeAlcanzado(){
		return (this.puntaje >= miPuntajeABatir);
	}
	
	//inserta los moviles en una cola
	private Queue<Movil> acolarMovilesParaVivir(){
		Iterator<Movil> iterador = this.listaDeMoviles.iterator();
		Queue<Movil> cola = new LinkedList<Movil>();
		while (iterador.hasNext()){
			Movil movil = iterador.next();
			cola.add(movil);
		}
		
		return cola;
	}

	
//	//le da a los moviles acolados el mensaje vivir. recibe Queue<Movil> cola 
//	private void vivirMovilesAcolados(Queue<Movil> cola){
//		
//		boolean seguir = true;
//		Movil actor;
//		while (seguir){
//			try{
//				//casteo
//				actor = (Automata) cola.remove();
//				if (actor.estaVivo()){
//					this.puntaje += actor.vivir(this.listaDeMoviles, this.listaDeBotines);
//				}
//			}
//			//si la cola esta vacia
//			catch(Exception e){
//				seguir = false;
//			}
//		}
//	}
	
	//le da a los moviles acolados el mensaje vivir. recibe Queue<Movil> cola 
	private void vivirMovilesAcolados(Queue<Movil> cola){
		boolean seguir = true;
		Movil movil;
		while (seguir){
			try{
				movil = cola.remove();
				if (movil.estaVivo()){
					this.puntaje += movil.vivir(this.listaDeMoviles, this.listaDeBotines);
				}
			}
			//si el tripulado esta muerto lanza la excepcion para arriba
			catch(ExcepcionMuerto em){
				throw em;
			}
			//si la cola esta vacia
			catch(Exception e){
				seguir = false;
			}
		}
	}
	
	
	
	//se encarga de que los moviles vivan
	//lanza una excepcionMuerto si el algo42 estaba muerto
	//lanza una excepcionPuntajeAlcanzado si se llego al puntaje para pasar de nivel
	//retorna una cola con todos los Posicionables vivos
	public Queue<Posicionable> vivir() {
		if(this.murio()){
			this.cantidadDeVidas--;
			throw new ExcepcionMuerto();
		}
		if (this.puntajeAlcanzado()){
			throw new ExcepcionPuntajeAlcanzado();
		}
		Queue<Movil> cola = this.acolarMovilesParaVivir();
		this.vivirMovilesAcolados(cola);
		//agrego los moviles a la lista de moviles.
		this.enlistarMoviles();
		return this.recolectarPosicionables();
	};

	
	private Queue<Posicionable> recolectarPosicionables() {
		Queue<Posicionable> cola = new LinkedList<Posicionable>();
		Iterator<Movil> iteradorMovil = this.listaDeMoviles.iterator();
		while (iteradorMovil.hasNext()){
			Posicionable posicionable = iteradorMovil.next();
			cola.add(posicionable);
		}
		Iterator<Botin> iteradorBotin = this.listaDeBotines.iterator();
		while (iteradorBotin.hasNext()){
			Posicionable posicionable = iteradorBotin.next();
			cola.add(posicionable);
		}
		return cola;
	}



	//agrega un movil a la cola de moviles para agregar
	public void agregarMovil(Movil movil){
		this.colaMovilesParaAgregar.add(movil);
	}
	
	//desacola los moviles y los agrega a la lista de Moviles
	private void enlistarMoviles(){
		this.listaDeMoviles.addAll(this.colaMovilesParaAgregar);
		this.colaMovilesParaAgregar.clear();
		
//		boolean seguir = true;
//		Movil movil;
//		while (seguir){
//			try{
//				movil = this.colaMovilesParaAgregar.remove();
//				this.listaDeMoviles.add(movil);
//			}
//			//si la cola esta vacia
//			catch(Exception e){
//				seguir = false;
//			}
//		}
	}

	

	//agrega una flota de moviles a la colaMovilesParaAgregar
	public void agregarFlotas(ArrayList<Movil> otraListaDeMoviles){
		Iterator<Movil> iterador = otraListaDeMoviles.iterator();
		while (iterador.hasNext()){
			Movil movil = iterador.next();
			this.colaMovilesParaAgregar.add(movil);
		};
	};
	
	public boolean quedanNavesEnemigas(){
		Iterator<Movil> iterador = this.listaDeMoviles.iterator();
		boolean unaNaveEnemigaEncontrada = false;
		while ((iterador.hasNext()) && (!unaNaveEnemigaEncontrada)){
			Movil movil = iterador.next();
			if (movil.esNaveEnemiga()){
				unaNaveEnemigaEncontrada = true;
			};
		};
		return unaNaveEnemigaEncontrada;	
	}



	public int verResistencia() {
		return this.algo42.verResistencia();
	}
	
	public int verCantidadDeVidas(){
		return this.cantidadDeVidas;
	}
	
	
	
	public void guardarDatos(){

		try {

			Document doc = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder().newDocument();
			
			@SuppressWarnings("unused")
			Element juego = this.getElement(doc);
			
//			Iterator<Movil> iteradorMoviles = this.listaDeMoviles.iterator();
//			Iterator<Botin> iteradorBotines = this.listaDeBotines.iterator();
//			
//			Element root = doc.createElement("Juego");
//			doc.appendChild(root);
//			
//			Element moviles = doc.createElement("Moviles");
//			root.appendChild(moviles);
//		
//			
//			while (iteradorMoviles.hasNext()){
//				Movil unMovil = iteradorMoviles.next();
//				Element element = unMovil.getElement(doc);
//				moviles.appendChild(element);
//				}
//			
//			Element botines = doc.createElement("Botines");
//			root.appendChild(botines);
//			
//			while (iteradorBotines.hasNext()){
//				Botin unBotin = iteradorBotines.next();
//				Element element = unBotin.getElement(doc);
//				botines.appendChild(element);
//				}
//			
//			Integer puntaje = new Integer(this.puntaje);
//			Element puntajeTotal = doc.createElement("Puntaje");
//			root.appendChild(puntajeTotal);
//			puntajeTotal.setTextContent(puntaje.toString());
			

			Transformer transformer = TransformerFactory.newInstance()
					.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.transform(new DOMSource(doc), new StreamResult(
					new PrintStream(archivo)));

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerFactoryConfigurationError e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
		
	  
	}
	
	
	//me parece que sera static este metodo

	public static Coordinador cargarDatos(){ 
		try {
			
			Document doc = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder().parse(new File(archivo));
			Element element = doc.getDocumentElement();		 
			
			//le paso el elemento "Juego"
			Coordinador coordinador = fromElement(element);
			return coordinador;
//			
//			ArrayList<Movil> lista = fromElement(element);
//			return lista;
	}
		catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		
		return null;
	}



	private static Coordinador fromElement(Element element) {
		NodeList childs = element.getChildNodes();
		int punt = 0;
		ArrayList<Movil> listaMovilesGuardados = new ArrayList<Movil>();;
		ArrayList<Botin> listaBotinesGuardados = new ArrayList<Botin>();;
		int puntajeGuardado;
		
		for (int i = 0; i < childs.getLength(); i++) {
			Node child = childs.item(i);
			System.out.println(child.getNodeName());
			if (child.getNodeName().equals("Moviles")) {
				listaMovilesGuardados = recuperarMoviles(child);
			}
			else if (child.getNodeName().equals("Botines")) {
				listaBotinesGuardados = recuperarBotines(child);
			}
			else if (child.getNodeName().equals("Puntaje")) {
				puntajeGuardado = Integer.parseInt(child.getTextContent());
				punt = puntajeGuardado;
			}
	
		}

		Coordinador coordinador = new Coordinador(listaMovilesGuardados,listaBotinesGuardados,punt);
		return coordinador;
	}



	private static ArrayList<Movil> recuperarMoviles(Node child) {
		NodeList listaNietos = child.getChildNodes();
		ArrayList<Movil> listaMovilesGuardados = new ArrayList<Movil>();
		
		for (int j = 0; j < listaNietos.getLength();j++) {
			Node nieto = listaNietos.item(j);
			Movil movil = recuperarMovilAlmacenado(nieto);
			listaMovilesGuardados.add(movil);
		}
		return listaMovilesGuardados;	
	}
	
	private static ArrayList<Botin> recuperarBotines(Node child) {
		NodeList listaNietos = child.getChildNodes();
		ArrayList<Botin> listaBotinesGuardados = new ArrayList<Botin>();
		
		for (int j = 0; j < listaNietos.getLength();j++) {
			Node nieto = listaNietos.item(j);
			Botin botin = recuperarBotinAlmacenado(nieto);
			listaBotinesGuardados.add(botin);
		}
		return listaBotinesGuardados;
		
	}

/*
 * esto se esta complicando demasiado. creo que lo mejor sera hacer un if else enorme
 */

	private static Movil recuperarMovilAlmacenado(Node nieto) {
		Movil nave = null;
		String nombreNieto = nieto.getNodeName();
		if (nombreNieto == "Avioneta"){
			Element element = (Element) nieto;
			nave = new Avioneta(0,0);
			nave = Avioneta.fromElement(element);		
		}else{
			if (nombreNieto == "Bombardero"){
				Element element = (Element) nieto;
				nave = new Bombardero(0,0);
				nave = Bombardero.fromElement(element);
			}else{
				if (nombreNieto == "Caza"){
					Element element = (Element) nieto;
					nave = new Caza(0,0);
					nave = Caza.fromElement(element);
				}else{
					if (nombreNieto == "AvionCivil"){
						Element element = (Element) nieto;
						nave = new AvionCivil(0,0);
						nave = AvionCivil.fromElement(element);
					}else{
						nave = new Tripulado();
					}
				}
			}
		}	
		return nave;
	}

	private static Botin recuperarBotinAlmacenado(Node nieto) {
		Botin botin = null;
		String nombreNieto = nieto.getNodeName();
		if (nombreNieto == "TanqueEnergia"){
			Element element = (Element) nieto;
			Coordenada c = new Coordenada(0,0);
			botin = new TanqueEnergia(c);
			botin = TanqueEnergia.fromElement(element);		
		}else{
			Coordenada c = new Coordenada(0,0);
			botin = new MejoraArmamento(c);
			}
		return botin;
	}



	@Override
	public Element getElement(Document doc) {
		Iterator<Movil> iteradorMoviles = this.listaDeMoviles.iterator();
		Iterator<Botin> iteradorBotines = this.listaDeBotines.iterator();
		
		Element root = doc.createElement("Juego");
		doc.appendChild(root);
		
		Element moviles = doc.createElement("Moviles");
		root.appendChild(moviles);
	
		
		while (iteradorMoviles.hasNext()){
			Movil unMovil = iteradorMoviles.next();
			Element element = unMovil.getElement(doc);
			moviles.appendChild(element);
			}
		
		Element botines = doc.createElement("Botines");
		root.appendChild(botines);
		
		while (iteradorBotines.hasNext()){
			Botin unBotin = iteradorBotines.next();
			Element element = unBotin.getElement(doc);
			botines.appendChild(element);
			}
		
		Integer puntaje = new Integer(this.puntaje);
		Element puntajeTotal = doc.createElement("Puntaje");
		root.appendChild(puntajeTotal);
		puntajeTotal.setTextContent(puntaje.toString());
		
		return root;
	}
	

	

	
	



	
	
		
}
